﻿using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace UT3Bots.Communications
{
    public class UT3Connection
    {
		public event EventHandler<TcpDataEventArgs> OnDataReceived;
        public event EventHandler<TcpErrorEventArgs> OnErrorOccurred;

        public String LastCommandSent { get; set; }
        public String LastMessageReceived { get; set; }

        private TcpClient _tcpClient;
        private StreamReader _streamReader;
        private StreamWriter _streamWriter;
        private NetworkStream _networkStream;

        private string _server;
        private int _port;

        internal bool IsConnected
        {
            get
            {
                return this._tcpClient != null && this._tcpClient.Connected;
            }
        }

        internal UT3Connection(string server, int port)
        {
            this._server = server;
            this._port = port;

            Connect();
        }

        internal bool Connect()
        {
            if (IsConnected)
            {
                Trace.WriteLine("Already Connected - Disconnect First", Global.TRACE_ERROR_CATEGORY);

                return false;
            }

            try
            {
                this._tcpClient = new TcpClient(this._server, this._port);

                _networkStream = this._tcpClient.GetStream();
                this._streamReader = new StreamReader(new StreamReader(_networkStream).BaseStream, System.Text.Encoding.ASCII);
                this._streamWriter = new StreamWriter(new StreamWriter(_networkStream).BaseStream, System.Text.Encoding.ASCII);

                Thread readThread = new Thread(new ThreadStart(ReadStream));
                readThread.Start();

                //Listen();

            }
            catch(Exception ex)
            {
                Trace.WriteLine(string.Format("The Connection Could Not Be Established: {0}.", ex.Message), Global.TRACE_ERROR_CATEGORY);

                if (this._streamReader != null)
                {
                    this._streamReader.Close();
                }
                if (this._streamWriter != null)
                {
                    this._streamWriter.Close();
                }

                //throw;
            }

            return this._streamReader != null && this._streamWriter != null;
        }

        private void ReadStream()
        {
            while (IsConnected)
            {
                try
                {
                    //Trace.WriteLine("Reading...");
                    String sData = _streamReader.ReadLine();
                    Trace.WriteLine(sData);
                    OnData(new TcpDataEventArgs(sData));
                }
                catch (IOException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        /*private void Listen()
        {
            SocketState state = new SocketState();
            state.stream = _networkStream;
            IAsyncResult ar = _networkStream.BeginRead(state.data, 0, state.data.Length, HandleRead, state);
        }

  
        internal void HandleRead(IAsyncResult ar)
        {
            SocketState state = (SocketState)ar.AsyncState;
            NetworkStream stream = state.stream;
            int r;

            if (!stream.CanRead)
                return;

            try
            {
                r = stream.EndRead(ar);
            }
            catch (Exception ex)
            {
                OnError(new TcpErrorEventArgs(ex));
				Trace.WriteLine(string.Format("TCP Connection Error: {0}.", ex.Message), Global.TRACE_NORMAL_CATEGORY);
				Disconnect();
                return;
            }

            byte[] rdata = new byte[r];
            Buffer.BlockCopy(state.data, 0, rdata, 0, r);

            OnData(new TcpDataEventArgs(rdata));

            try
            {
                Listen();
            }
            catch (Exception ex)
            {
                OnError(new TcpErrorEventArgs(ex));
            }
        }*/

        void OnData(TcpDataEventArgs args)
        {
            EventHandler<TcpDataEventArgs> temp = OnDataReceived;
            if (temp != null)
            {
                temp(this, args);
            }
        }

        void OnError(TcpErrorEventArgs args)
        {
            EventHandler<TcpErrorEventArgs> temp = OnErrorOccurred;
            if (temp != null)
            {
                temp(this, args);
            }
        }

		internal void Disconnect()
		{
            Console.WriteLine("Disconnecting");
			if (this._tcpClient.Client.Connected)
			{
				this._tcpClient.Client.Disconnect(false);
				this._tcpClient.Client.Close();
				this._tcpClient.Close();
			}

			this._streamReader.Close();
		}

       /* internal string ReadLine()
        {
            if (this._streamReader != null)
            {
                string line = this._streamReader.ReadLine();
                Trace.WriteLine(string.Format("Received Server Line: {0}.", line), Global.TRACE_NORMAL_CATEGORY);
                LastMessageReceived = line;
                return line;
            }
            else
            {
                Trace.WriteLine("Error reading from connection!", Global.TRACE_ERROR_CATEGORY);
                LastMessageReceived = "";
                return "";
            }
        }
        */
        internal void SendLine(string Message)
        {
            if (this.IsConnected)
            {
                try
                {
                    Trace.WriteLine(string.Format("Sending Server Line: {0}.", Message), Global.TRACE_NORMAL_CATEGORY);

                    this._streamWriter.WriteLine(Message);
                    this._streamWriter.Flush();
                    LastCommandSent = Message;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            else
            {
                Trace.WriteLine("Error writing to connection!", Global.TRACE_ERROR_CATEGORY);
            }
        }
    }


    class SocketState
    {
        public byte[] data = new byte[300];
        public NetworkStream stream;
    }

	public class TcpDataEventArgs : EventArgs
	{
		private byte[] _data;
        public string sData { get; set; }
		#region Properties
		public byte[] Data
		{
			get { return this._data; }
		}
		#endregion

		public TcpDataEventArgs(byte[] Data)
		{
			this._data = Data;
		}

        public TcpDataEventArgs(string data)
        {
            this.sData = data;
        }
	}

	public class TcpErrorEventArgs : EventArgs
	{
		private Exception _error;

		#region Properties
		public Exception Error
		{
			get { return this._error; }
		}
		#endregion

		public TcpErrorEventArgs(Exception Error)
		{
			this._error = Error;
		}
	}
}

